/*
 * Copyright (c) 2016. 恒昌互联网运营中心.
 */
package com.credithc.eagleeye.api.service.impl;
import com.credithc.eagleeye.api.factory.ESFactory;
import com.credithc.eagleeye.api.protocol.TraceInfoSearchRes;
import com.credithc.eagleeye.api.service.TraceInfoSearchService;
import com.credithc.eagleeye.api.vo.*;
import com.credithc.eagleeye.vo.RpcTraceInfoVo;
import com.credithc.framework.es.util.ElasticSearchUtil;
import com.credithc.framework.utils.date.DateTools;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.*;
/**
 * 描 述: <br/>
 * 作 者: zhangjin<br/>
 * 创 建：2016年06月21日<br/>
 * 版 本：v1.0.0<br>
 * <p/>
 * 历 史: (版本) 作者 时间 注释 <br/>
 */
@Service
@SuppressWarnings("Duplicates")
public class TraceInfoSearchServiceImpl implements TraceInfoSearchService {
    public static final String EAGLE_MONITOR_ES_INDEX = "eagle-monitor-*";
    public static final String EAGLE_MONITOR_ES_TYPE = "eagle";
    @Override
    public TraceInfoSearchRes searchTraceInfoList(TraceInfoSearchConditionVo conditionVo) {
        ElasticSearchUtil elasticSearchUtil = ESFactory.getInstance();
        long toTime = 0;
        long formTime = 0;
        if (StringUtils.isNotBlank(conditionVo.getFromDate())) {
            formTime = DateTools.Date19ToDate(conditionVo.getFromDate()).getTime();
        }
        if (StringUtils.isNotBlank(conditionVo.getToDate())) {
            toTime = DateTools.Date19ToDate(conditionVo.getToDate()).getTime();
        }
        int pageSize = conditionVo.getPageSize();
        int from = pageSize * (conditionVo.getPageNum() - 1);
        //搜索条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isNotBlank(conditionVo.getTraceId())) {
            boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("traceId", StringUtils.trimToEmpty(conditionVo.getTraceId())));
            if (StringUtils.isNotBlank(conditionVo.getRpcId())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("rpcId", StringUtils.trimToEmpty(conditionVo.getRpcId())));
            }
        } else {
            if (StringUtils.isNotBlank(conditionVo.getPlatformName())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("platformName.raw", StringUtils.trimToEmpty(conditionVo.getPlatformName())));
            }
            if (StringUtils.isNotBlank(conditionVo.getUserInfoType()) && StringUtils.isNotBlank(conditionVo.getUserInfoValue())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("userInfo." + conditionVo.getUserInfoType(), conditionVo.getUserInfoValue()));
            }
            if (StringUtils.isNotBlank(conditionVo.getEnv())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("env", StringUtils.trimToEmpty(conditionVo.getEnv())));
            }
            if (StringUtils.isNotBlank(conditionVo.getRpcId())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("rpcId", StringUtils.trimToEmpty(conditionVo.getRpcId())));
            }
            if (StringUtils.isNotBlank(conditionVo.getRpcType())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("rpcType", conditionVo.getRpcType()));
            }
            if (StringUtils.isNotBlank(conditionVo.getClientName())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("clientName", StringUtils.trimToEmpty(conditionVo.getClientName())));
            }
            if (StringUtils.isNotBlank(conditionVo.getServiceCategory())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("serviceCategory", StringUtils.trimToEmpty(conditionVo.getServiceCategory())));
            }
            if (StringUtils.isNotBlank(conditionVo.getServiceName())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("serviceName", StringUtils.trimToEmpty(conditionVo.getServiceName())));
            }
            if (StringUtils.isNotBlank(conditionVo.getMethodName())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("methodName", StringUtils.trimToEmpty(conditionVo.getMethodName())));
            }
            if (StringUtils.isNotBlank(conditionVo.getResult())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("result", StringUtils.trimToEmpty(conditionVo.getResult())));
            }
            //时间过滤
            if (StringUtils.isNotBlank(conditionVo.getRunTimeMin())) {
                boolQueryBuilder.must(QueryBuilders.rangeQuery("runTime").from(Long.parseLong(conditionVo.getRunTimeMin())));
            }
            if (StringUtils.isNotBlank(conditionVo.getRunTimeMax())) {
                boolQueryBuilder.must(QueryBuilders.rangeQuery("runTime").to(Long.parseLong(conditionVo.getRunTimeMax())));
            }
            //入参
            if (StringUtils.isNotBlank(conditionVo.getParamIn())) {
                String arr[] = StringUtils.split(conditionVo.getParamIn(), "&&");
                for(String paramIn:arr) {
                    boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("requestJson", StringUtils.trimToEmpty(paramIn)));
                }
            }
            //出参
            if (StringUtils.isNotBlank(conditionVo.getParamOut())) {
                String arr[] = StringUtils.split(conditionVo.getParamOut(), "&&");
                for(String paramOut:arr) {
                    boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("responseJson", StringUtils.trimToEmpty(paramOut)));
                }
            }
            //入参大小
            if (StringUtils.isNotBlank(conditionVo.getRequestSizeMin())) {
                boolQueryBuilder.must(QueryBuilders.rangeQuery("requestSize").from(Long.parseLong(conditionVo.getRequestSizeMin())*1024));
            }
            if (StringUtils.isNotBlank(conditionVo.getRequestSizeMax())) {
                boolQueryBuilder.must(QueryBuilders.rangeQuery("requestSize").to(Long.parseLong(conditionVo.getRequestSizeMax())*1024));
            }
            //出参大小
            if (StringUtils.isNotBlank(conditionVo.getResponseSizeMin())) {
                boolQueryBuilder.must(QueryBuilders.rangeQuery("responseSize").from(Long.parseLong(conditionVo.getResponseSizeMin())*1024));
            }
            if (StringUtils.isNotBlank(conditionVo.getRequestSizeMax())) {
                boolQueryBuilder.must(QueryBuilders.rangeQuery("responseSize").to(Long.parseLong(conditionVo.getResponseSizeMax())*1024));
            }
            //IP
            if (StringUtils.isNotBlank(conditionVo.getClientIP())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("clientHost", conditionVo.getClientIP()));
            }
            if (StringUtils.isNotBlank(conditionVo.getServerIP())) {
                boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("serverHost", conditionVo.getServerIP()));
            }
        }
        //2.x里不能用过滤器了
        if (formTime != 0) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("requestDateTime").from(formTime));
        }
        if (toTime != 0) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("requestDateTime").to(toTime));
        }
        //排除定时任务
        boolQueryBuilder.mustNot(QueryBuilders.matchPhraseQuery("serviceName", "taskIceService"));
        List<SortBuilder> sortBuilderList = new ArrayList<>();
        SortBuilder sortBuilder0 = SortBuilders.fieldSort("requestDateTime").order(SortOrder.DESC);
        sortBuilderList.add(sortBuilder0);

        List<RpcTraceInfoVo> list = elasticSearchUtil.search(RpcTraceInfoVo.class, EAGLE_MONITOR_ES_INDEX, EAGLE_MONITOR_ES_TYPE, boolQueryBuilder, sortBuilderList, from, pageSize);
        if (StringUtils.isNotBlank(conditionVo.getTraceId())) {
            Collections.sort(list, new RpcTraceInfoVoComparator());
        }
        for(RpcTraceInfoVo vo : list) {
            String requestDateTime = vo.getRequestDateTime();
            if(requestDateTime.indexOf("T")>-1) {
                vo.setRequestDateTime(requestDateTime.replace("T", " ").substring(0, 23));
            }else if(StringUtils.isNumeric(requestDateTime)){
                vo.setRequestDateTime(DateTools.FormatDate19(new Date(Long.parseLong(requestDateTime))));
            }
        }
        long count = elasticSearchUtil.count(EAGLE_MONITOR_ES_INDEX, EAGLE_MONITOR_ES_TYPE, boolQueryBuilder);


        TraceInfoSearchRes traceInfoSearchRes = new TraceInfoSearchRes();
        traceInfoSearchRes.setRpcTraceInfoVoList(buildTraceInfoTree(list,conditionVo.getTraceId()));
        traceInfoSearchRes.setTotalCount(count);
        traceInfoSearchRes.setPageNum(conditionVo.getPageNum());
        traceInfoSearchRes.setPageSize(conditionVo.getPageSize());
        return traceInfoSearchRes;
    }


    private List<RpcTraceInfoTreeVo> buildTraceInfoTree(List<RpcTraceInfoVo> list,String traceId){
        List<RpcTraceInfoTreeVo> retList = new ArrayList<>();

        HashMap <String, RpcTraceInfoTreeVo> nodeMap = new HashMap<>(list.size());

        for(RpcTraceInfoVo rpcTraceInfoVo:list){
            RpcTraceInfoTreeVo temp = new RpcTraceInfoTreeVo();
            BeanUtils.copyProperties(rpcTraceInfoVo,temp);
            if(StringUtils.isBlank(traceId)) {
                retList.add(temp);
            }else{
                String parentId = StringUtils.substringBeforeLast(temp.getRpcId(),".");
                RpcTraceInfoTreeVo parentVo = nodeMap.get(parentId);
                if (parentVo!=null) {
                    parentVo.getChildren().add(temp);
                }else{
                    retList.add(temp);
                }
                nodeMap.put(temp.getRpcId(), temp);
            }
        }
        return retList;
    }

    @Override
    public JavaLogSearchRes searchJavaLogList(JavaLogSearchConditionVo conditionVo) {
        ElasticSearchUtil elasticSearchUtil = ESFactory.getInstance();
        //搜索条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isNotBlank(conditionVo.getTraceId())) {
            boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("traceId", conditionVo.getTraceId()));
        }
        if (StringUtils.isNotBlank(conditionVo.getRpcId())) {
            String rpcId = conditionVo.getRpcId();
            BoolQueryBuilder rpcIdBoolQueryBuilder = QueryBuilders.boolQuery();
            if(rpcId.equals("1")){
                rpcIdBoolQueryBuilder.should(QueryBuilders.matchPhraseQuery("rpcId", "0"));
            }
            if (rpcId.length() == 1) {
                rpcId=rpcId+".0";
            }
            rpcIdBoolQueryBuilder.should(QueryBuilders.matchPhraseQuery("rpcId", rpcId));

            String lastId=StringUtils.substring(rpcId,StringUtils.lastIndexOf(rpcId,".")+1);
            if(StringUtils.equals(lastId,"1")) {
                String tempRpcId = StringUtils.substring(rpcId, 0, StringUtils.lastIndexOf(rpcId, "."))+".0";
                rpcIdBoolQueryBuilder.should(QueryBuilders.matchPhraseQuery("rpcId", tempRpcId));
            }

            boolQueryBuilder.must(rpcIdBoolQueryBuilder);
        }
        long toTime = 0;
        long formTime = 0;
        if (StringUtils.isNotBlank(conditionVo.getFromDate())) {
            formTime = DateTools.Date10ToDate(DateTools.StringDateTo10(conditionVo.getFromDate())).getTime();
            toTime = formTime + 60 * 60 * 24 * 1000;
        }
        if (formTime != 0) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("@timestamp").from(formTime));
        }
        if (toTime != 0) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("@timestamp").to(toTime));
        }
        List<SortBuilder> sortBuilderList = new ArrayList<>();
        SortBuilder sortBuilder0 = SortBuilders.fieldSort("@timestamp").order(SortOrder.ASC);
        sortBuilderList.add(sortBuilder0);
        int pageSize = 500;
        int from = 0;
        List<JavaLogVo> list = elasticSearchUtil.search(JavaLogVo.class, "java-log-*", "", boolQueryBuilder, sortBuilderList, from, pageSize);
        for(JavaLogVo vo : list) {
            vo.setTimestamp(vo.getTimestamp().replace("T", " ").substring(0, 23));
        }
        JavaLogSearchRes javaLogSearchRes = new JavaLogSearchRes();
        javaLogSearchRes.setJavaLogVoList(list);
        return javaLogSearchRes;
    }
}

@SuppressWarnings("Duplicates")
class RpcTraceInfoVoComparator implements Comparator {
    /**
     * 实现compare
     */
    public int compare(Object o1, Object o2) {
        RpcTraceInfoVo r1 = (RpcTraceInfoVo) o1;
        RpcTraceInfoVo r2 = (RpcTraceInfoVo) o2;
        // 3个属性的比较
        int flag1 = r1.getTraceId().compareTo(r2.getTraceId());
//        int flag2 = r1.getRpcId().compareTo(r2.getRpcId());
        String r1Arr[] = r1.getRpcId().split("\\.");
        String r2Arr[] = r2.getRpcId().split("\\.");
        int compareCount = (r1Arr.length > r2Arr.length) ? r2Arr.length : r1Arr.length;
        int flag2 = 0;
        for(int i = 0; i < compareCount; i++) {
            int a = Integer.parseInt(r1Arr[i]);
            int b = Integer.parseInt(r2Arr[i]);
            if (a > b) {
                flag2 = 1;
                break;
            } else if (a < b) {
                flag2 = -1;
                break;
            }
        }
        if (flag2 == 0) {
            flag2 = (r1Arr.length > r2Arr.length) ? 1 : -1;
        }
        // 第一个相等，比较第二个
        if (flag1 == 0) {
            return flag2;
        } else {
            return flag1;
        }
    }
}
